package service

import (
	"cap/dao"
	"encoding/json"
	"fmt"
	"github.com/jinzhu/copier"
	"gorm.io/gorm"
	"lib/id"
	"lib/jwt"
	"lib/structs/cap"
	"lib/structs/tables"
	"lib/utils"
	"time"
)

var HostSer = new(hostService)

type hostService struct {
}

func (s hostService) NoticeHostRegister(hostId int64) (err error) {
	var hostInfo tables.CapHost
	err = dao.DB.Where("id = ?", hostId).Find(&hostInfo).Error
	if err != nil {
		return err
	}
	_, err = utils.HttpClient.Post(fmt.Sprintf("%s%s", s.getMetaData(hostInfo.Meta, "api"), "/client/register"), nil, nil)
	return err
}
func (s hostService) NoticeHostUploadApps(hostId int64) (err error) {
	var hostInfo tables.CapHost
	err = dao.DB.Where("id = ?", hostId).Find(&hostInfo).Error
	if err != nil {
		return err
	}
	_, err = utils.HttpClient.Post(fmt.Sprintf("%s%s", s.getMetaData(hostInfo.Meta, "api"), "/client/uploadCloudApp"), nil, nil)
	return err
}

func (s hostService) BindCluster(hostId, clusterId int64) (err error) {
	err = dao.HostDao.BindClusterId(hostId, clusterId)
	return err
}

// AppIssue cap 应用发布，发布时只能按集群发布。也就是说应用主机必须绑定集群
func (s hostService) AppIssue(in cap.AppIssueForm) (err error) {
	var (
		culInfo tables.CapCluster
	)

	err = dao.DB.Where("id = ?", in.ClusterId).First(&culInfo).Error
	if err != nil {
		return err
	}
	metaMap := make(map[string]string)
	metaMap["name"] = in.AppName
	metaMap["img"] = in.Icon
	metaMap["appName"] = in.AppName
	metaMap["icon"] = in.Icon
	metaMap["exePath"] = in.ExePath
	metaMap["appParams"] = in.AppParams
	metaMap["workSpace"] = in.WorkSpace
	metaMap["isSingle"] = fmt.Sprintf("%t", in.IsSingle)

	links := make([]tables.CapLink, len(in.UserIds))
	for i, uid := range in.UserIds {
		item := tables.CapLink{
			Id:        id.GetSnowId(),
			UserId:    uid,
			LinkType:  "cap",      // 默认cap协议
			SourceId:  culInfo.Id, // 填充集群ID，要求主机必须加入集群
			MetaData:  utils.Obj2Str(metaMap),
			CertifyId: 0, // cap 协议使用计算生成的凭证，特殊应用可以解析 MetaData 后使用管理员凭证,要习惯使用非结构化数据
		}
		links[i] = item
	}
	err = dao.DB.Create(&links).Error
	return err
}
func (s hostService) AppList() (li []cap.AppCollectDto, err error) {
	var ali []tables.CapAppCollect
	err = dao.DB.Find(&ali).Error
	if err != nil {
		return
	}
	hostMap := s.getHostMap()
	cluMap := s.getClusterMap()
	li = make([]cap.AppCollectDto, len(ali))
	for i, item := range ali {
		dto := cap.AppCollectDto{
			AppInfo:  item,
			HostInfo: hostMap[item.HostId],
		}
		if hostMap[item.HostId].ClusterId != 0 {
			dto.ClusterInfo = cluMap[hostMap[item.HostId].ClusterId]
		}
		li[i] = dto
	}
	return li, nil
}

func (s hostService) AddHost(in cap.AddHostForm, me *jwt.CustomClaims) (err error) {
	err = dao.DB.Transaction(func(tx *gorm.DB) error {
		hm := tables.CapHost{
			Id:           id.GetSnowId(),
			Uid:          in.Uid,
			Ip:           in.Ip,
			GuaPort:      in.GuaPort,
			Status:       in.Status,
			GuaProtocol:  in.GuaProtocol,
			ClusterId:    in.ClusterId,
			BindUserID:   in.BindUserID,
			LockedUserID: 0,
			Meta:         "",
			CreatedAt:    time.Now().Unix(),
		}
		err = tx.Create(&hm).Error
		if err != nil {
			return err
		}
		// todo 附加 meta 信息
		// 如果是输入了账号密码，就同步添加凭证信息
		if in.Account != "" && in.Password != "" {
			cm := tables.CapCertify{
				Id: id.GetSnowId(),
				//Type:      "",
				SourceId:    hm.Id,
				Account:     in.Account,
				Password:    in.Password,
				Description: "", // todo 附加凭证描述信息，因为凭证直接显示账号、密码不合适，显示账号、描述比较好
				Creator:     me.Nickname,
				CreatorId:   me.UserID,
				CreatedAt:   time.Now().Unix(),
			}
			switch in.GuaProtocol {
			case "rdp":
				cm.Type = tables.CertifyTypeForRDP
			case "vnc":
				cm.Type = tables.CertifyTypeForVNC
			case "sh":
				cm.Type = tables.CertifyTypeForSH
			default:
				err = fmt.Errorf("未定义的协议类型")
				return err
			}
			err = tx.Create(&cm).Error
			if err != nil {
				return err
			}
		}
		return nil
	})
	return err
}

func (s hostService) FindPage(in cap.SearchHostForm) (li []cap.HostDto, total int64, err error) {
	var hlist []tables.CapHost
	hlist, total, err = dao.HostDao.Page(in)
	if err != nil {
		return
	}
	clusterMap := s.getClusterMap()
	for _, item := range hlist {
		li = append(li, cap.HostDto{
			HostInfo:    item,
			ClusterInfo: clusterMap[item.ClusterId],
		})
	}
	return
}

func (s hostService) Delete(id int64, me *jwt.CustomClaims) (err error) {
	if me.AccountLv != tables.AccountLvForRoot {
		err = fmt.Errorf("权限不足")
		return err
	}
	err = dao.DB.Transaction(func(tx *gorm.DB) error {
		err = tx.Where(&tables.CapHost{Id: id}).Delete(&tables.CapHost{}).Error
		if err != nil {
			return err
		}
		err = tx.Where(&tables.CapCertify{SourceId: id}).Delete(&tables.CapCertify{}).Error
		if err != nil {
			return err
		}
		return nil
	})
	return err
}

func (s hostService) AddCluster(in cap.AddClusterForm, me *jwt.CustomClaims) (err error) {
	err = dao.DB.Transaction(func(tx *gorm.DB) error {
		cm := tables.CapCluster{
			Id:              id.GetSnowId(),
			Name:            in.Name,
			CpuThreshold:    in.CpuThreshold,
			MemoryThreshold: in.MemoryThreshold,
			MaxSessionTime:  in.MaxSessionTime,
			SessionOutTime:  in.SessionOutTime,
			TimeOutAction:   in.TimeOutAction,
			ClusterType:     in.ClusterType,
			Creator:         me.Nickname,
			CreatorId:       me.UserID,
			CreatedAt:       time.Now().Unix(),
		}
		err = tx.Create(&cm).Error
		if err != nil {
			return err
		}
		return nil
	})
	return err
}

func (s hostService) UpdateCluster(in cap.AddClusterForm, me *jwt.CustomClaims) (err error) {
	if me.AccountLv != tables.AccountLvForRoot {
		err = fmt.Errorf("权限不足")
		return err
	}
	var clu tables.CapCluster
	err = dao.DB.Model(&tables.CapCluster{}).Where("id=?", in.Id).Find(&clu).Error
	if err != nil {
		return err
	}
	err = copier.Copy(&clu, in)
	if err != nil {
		return err
	}
	err = dao.DB.Where("id = ?", clu.Id).Updates(&clu).Error
	return err
}

func (s hostService) FindClusterList() (li []tables.CapCluster, err error) {
	li, err = dao.HostDao.ClusterList()
	return
}

func (s hostService) FindClusterDtoList() (li []cap.ClusterDto, err error) {
	var cluList []tables.CapCluster
	cluList, err = dao.HostDao.ClusterList()
	if err != nil {
		return
	}
	cmli := s.getClusterMapHostList()
	for _, item := range cluList {
		li = append(li, cap.ClusterDto{
			ClusterInfo: item,
			HostList:    cmli[item.Id],
		})
	}
	return
}

func (s hostService) DelCluster(id int64, me *jwt.CustomClaims) (err error) {
	var num int64
	num, err = dao.HostDao.CountHostNumByClusterId(id)
	if err != nil {
		return
	}
	if num > 0 {
		err = fmt.Errorf("集群下还有主机，不能删除")
		return err
	}
	if me.AccountLv != tables.AccountLvForRoot {
		err = fmt.Errorf("权限不足")
		return err
	}
	err = dao.DB.Where(&tables.CapCluster{Id: id}).Delete(&tables.CapCluster{}).Error
	return err
}

// LinkBindUser 链接绑定用户
func (s hostService) LinkBindUser(in cap.AddLinkDto) (err error) {
	var host tables.CapHost
	host, err = dao.HostDao.FindById(in.SourceId)
	if err != nil {
		return err
	}
	m := tables.CapLink{
		Id:        id.GetSnowId(),
		UserId:    in.UserId,
		LinkType:  host.GuaProtocol,
		SourceId:  host.Id,
		MetaData:  in.MetaData,
		CertifyId: in.CertifyId,
	}
	err = dao.DB.Create(&m).Error
	if err != nil {
		return err
	}
	return err
}

func (s hostService) LinkList(me jwt.CustomClaims) (li []cap.LinkDto, err error) {
	var linkList []tables.CapLink
	err = dao.DB.Model(&tables.CapLink{}).Where("user_id = ?", me.UserID).Find(&linkList).Error
	if err != nil {
		return
	}
	li = make([]cap.LinkDto, len(linkList))
	hostMap := s.getHostMap()
	clusterMap := s.getClusterMap()
	for i, item := range linkList {
		dtoItem := cap.LinkDto{
			LinkInfo: item,
		}
		switch item.LinkType {
		case "vnc":
			dtoItem.Name = s.getMetaData(item.MetaData, "name")
			dtoItem.Img = s.getMetaData(item.MetaData, "img")
			dtoItem.Protocol = "vnc"
			dtoItem.Title = hostMap[item.SourceId].Ip
			dtoItem.Subtitle = "vnc"
		case "sh":
			dtoItem.Name = s.getMetaData(item.MetaData, "name")
			dtoItem.Img = s.getMetaData(item.MetaData, "img")
			dtoItem.Protocol = "sh"
			dtoItem.Title = hostMap[item.SourceId].Ip
			dtoItem.Subtitle = "sh"
		case "rdp":
			dtoItem.Name = s.getMetaData(item.MetaData, "name")
			dtoItem.Img = s.getMetaData(item.MetaData, "img")
			dtoItem.Protocol = "rdp"
			dtoItem.Title = hostMap[item.SourceId].Ip
			dtoItem.Subtitle = "rdp"
		case "cap":
			dtoItem.Name = s.getMetaData(item.MetaData, "name")
			dtoItem.Img = s.getMetaData(item.MetaData, "img")
			dtoItem.Protocol = "cap"
			dtoItem.Title = clusterMap[item.SourceId].Name
			switch clusterMap[item.SourceId].ClusterType {
			case 1:
				dtoItem.Subtitle = "私有"
			case 2:
				dtoItem.Subtitle = "抢占"
			case 3:
				dtoItem.Subtitle = "共享"
			default:
				dtoItem.Subtitle = "未知"
			}
		default:
			err = fmt.Errorf("未支持的协议类型")
			return
		}
		li[i] = dtoItem
	}
	return li, nil
}

func (s hostService) GetLinkDetail(linkId int64) (dtoItem cap.LinkDto, err error) {
	var item tables.CapLink
	err = dao.DB.Model(&tables.CapLink{}).Where("id = ?", linkId).First(&item).Error
	if err != nil {
		return
	}
	hostMap := s.getHostMap()
	clusterMap := s.getClusterMap()
	dtoItem = cap.LinkDto{
		LinkInfo: item,
	}
	switch item.LinkType {
	case "vnc":
		dtoItem.Name = s.getMetaData(item.MetaData, "name")
		dtoItem.Img = s.getMetaData(item.MetaData, "img")
		dtoItem.Protocol = "vnc"
		dtoItem.Title = hostMap[item.SourceId].Ip
		dtoItem.Subtitle = "vnc"
	case "sh":
		dtoItem.Name = s.getMetaData(item.MetaData, "name")
		dtoItem.Img = s.getMetaData(item.MetaData, "img")
		dtoItem.Protocol = "sh"
		dtoItem.Title = hostMap[item.SourceId].Ip
		dtoItem.Subtitle = "sh"
	case "rdp":
		dtoItem.Name = s.getMetaData(item.MetaData, "name")
		dtoItem.Img = s.getMetaData(item.MetaData, "img")
		dtoItem.Protocol = "rdp"
		dtoItem.Title = hostMap[item.SourceId].Ip
		dtoItem.Subtitle = "rdp"
	case "cap":
		dtoItem.Name = s.getMetaData(item.MetaData, "name")
		dtoItem.Img = s.getMetaData(item.MetaData, "img")
		dtoItem.Protocol = "cap"
		dtoItem.Title = clusterMap[item.SourceId].Name
		switch clusterMap[item.SourceId].ClusterType {
		case 1:
			dtoItem.Subtitle = "私有"
		case 2:
			dtoItem.Subtitle = "抢占"
		case 3:
			dtoItem.Subtitle = "共享"
		default:
			dtoItem.Subtitle = "未知"
		}
	default:
		err = fmt.Errorf("未支持的协议类型")
		return
	}
	return dtoItem, nil
}

func (s hostService) GetCertifyList(me jwt.CustomClaims) (li []tables.CapCertify, err error) {
	err = dao.DB.Model(&tables.CapCertify{}).Where("creator_id = ?", me.UserID).Find(&li).Error
	for i := range li {
		li[i].Password = ""
	}
	return
}

func (s hostService) CertifyPage(in cap.SearchCertifyForm) (li []tables.CapCertify, total int64, err error) {
	tx := dao.DB.Model(&tables.CapCertify{})
	if in.Account != "" {
		tx.Where("account like ?", "%"+in.Account+"%")
	}
	if in.Type != "" {
		tx.Where("type = ?", in.Type)
	}
	err = tx.Count(&total).Offset((in.PageNum - 1) * in.PageSize).Limit(in.PageSize).Find(&li).Error
	if err != nil {
		return
	}
	return
}

func (s hostService) getMetaData(metaStr string, key string) (val string) {
	metaMap := make(map[string]string)
	err := json.Unmarshal([]byte(metaStr), &metaMap)
	if err != nil {
		return
	}
	val = metaMap[key]
	return
}

func (s hostService) getClusterMap() (clusterMap map[int64]tables.CapCluster) {
	li, err := dao.HostDao.ClusterList()
	if err != nil {
		return
	}
	clusterMap = make(map[int64]tables.CapCluster)
	for _, item := range li {
		clusterMap[item.Id] = item
	}
	return
}

func (s hostService) getHostMap() (hostMap map[int64]tables.CapHost) {
	li, err := dao.HostDao.HostList()
	if err != nil {
		return
	}
	hostMap = make(map[int64]tables.CapHost)
	for _, item := range li {
		hostMap[item.Id] = item
	}
	return
}
func (s hostService) getClusterMapHostList() (mm map[int64][]tables.CapHost) {
	mm = make(map[int64][]tables.CapHost)
	hostLi, err := dao.HostDao.HostList()
	if err != nil {
		return
	}
	for _, item := range hostLi {
		if _, ok := mm[item.ClusterId]; !ok {
			mm[item.ClusterId] = []tables.CapHost{item}
		} else {
			mm[item.ClusterId] = append(mm[item.ClusterId], item)
		}
	}
	return mm
}
